<!DOCTYPE html>
<html lang="zh-CN" data-bs-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MyDBMS Pro</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/codemirror.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/addon/hint/show-hint.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/theme/monokai.css">

    <style>
        :root {
            --sidebar-width: 280px;
        }
        html, body {
            height: 100%;
            overflow: hidden;
        }
        body {
            display: flex;
        }
        .sidebar {
            width: var(--sidebar-width);
            flex-shrink: 0;
        }
        .main-content {
            flex-grow: 1;
            padding: 1rem;
            display: flex;
            flex-direction: column;
            height: 100vh;
        }

        /* --- 全新的布局替换方案 --- */
        .main-layout-grid {
            display: grid;
            grid-template-rows: auto 1fr; /* 标题行自适应高度, 内容行填满剩余空间 */
            height: 100%;
            overflow: hidden;
        }
        #main-tabs-content {
            overflow: hidden; /* 防止双重滚动条 */
        }
        /* 强制隐藏非激活的 Tab, 彻底解决占位问题 */
        .tab-content > .tab-pane {
            display: none;
            height: 100%; /* 确保 pane 继承父级高度 */
        }
        .tab-content > .active {
            display: block;
        }
        /* --- 布局替换结束 --- */

        .sidebar .nav-link { cursor: pointer; }
        .pagination .page-link { cursor: pointer; }
        .theme-switch-btn .bi-sun-fill { display: none; }
        .theme-switch-btn .bi-moon-stars-fill { display: block; }
        [data-bs-theme="dark"] .theme-switch-btn .bi-sun-fill { display: block; }
        [data-bs-theme="dark"] .theme-switch-btn .bi-moon-stars-fill { display: none; }

        .editor-layout {
            height: 100%;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }
        .editor-layout > .card {
            display: flex;
            flex-direction: column;
            min-height: 0;
        }
        .editor-card { flex: 1 1 50%; } /* 编辑器和结果各占约一半 */
        .results-card { flex: 1 1 50%; }
        .card .card-body {
            flex-grow: 1;
            overflow: auto;
            position: relative;
        }
        .editor-container, .CodeMirror {
            height: 100% !important;
        }
        #sql-editor { display: none; }
    </style>
</head>
<body>

<aside class="sidebar d-flex flex-column p-3 bg-body-tertiary border-end">
    <div class="d-flex justify-content-between align-items-center">
        <h4 class="mb-0"><i class="bi bi-database"></i> 数据库对象</h4>
        <button class="btn btn-sm btn-outline-secondary" id="refresh-schema-btn" title="刷新"><i class="bi bi-arrow-clockwise"></i></button>
    </div>
    <hr>
    <ul class="nav nav-pills flex-column mb-auto" id="db-objects-list"></ul>
    <hr>
    <div>
        <button class="btn btn-link text-decoration-none w-100 d-flex justify-content-start align-items-center ps-2" id="theme-switcher">
             <span class="theme-switch-btn me-2 fs-5"><i class="bi bi-sun-fill"></i><i class="bi bi-moon-stars-fill"></i></span>
             <span id="theme-text">切换主题</span>
        </button>
    </div>
</aside>

<main class="main-content">
    <div class="main-layout-grid">
        <ul class="nav nav-tabs" id="main-tabs" role="tablist">
            <li class="nav-item" role="presentation">
                <button class="nav-link active" id="data-view-tab" data-bs-toggle="tab" data-bs-target="#data-view" type="button" role="tab"><i class="bi bi-table me-2"></i>数据浏览</button>
            </li>
            <li class="nav-item" role="presentation">
                <button class="nav-link" id="query-editor-tab" data-bs-toggle="tab" data-bs-target="#query-editor-view" type="button" role="tab"><i class="bi bi-terminal me-2"></i>查询编辑器</button>
            </li>
        </ul>

        <div class="tab-content pt-3" id="main-tabs-content">
            <div class="tab-pane fade show active" id="data-view" role="tabpanel">
                <div class="card h-100 d-flex flex-column">
                    <div class="card-header">
                        <h5 id="table-view-title" class="mb-0">请从左侧选择一个表进行浏览</h5>
                    </div>
                    <div class="card-body p-0">
                        <div class="table-responsive h-100" id="data-browser-container">
                            </div>
                    </div>
                    <div class="card-footer" id="pagination-container" style="display: none;">
                         <nav id="pagination-nav" aria-label="Page navigation"></nav>
                    </div>
                </div>
            </div>

            <div class="tab-pane fade" id="query-editor-view" role="tabpanel">
                 <div class="editor-layout">
                    <div class="card editor-card">
                         <div class="card-body p-0 editor-container">
                             <textarea id="sql-editor" placeholder="在此输入SQL..."></textarea>
                         </div>
                         <div class="card-footer d-flex justify-content-between align-items-center py-2">
                             <div>
                                 <button class="btn btn-primary" id="execute-btn" title="执行 (Ctrl+Enter)"><i class="bi bi-play-fill"></i> 执行</button>
                                 <button class="btn btn-success" id="execute-selected-btn" title="执行选中部分 (Ctrl+Shift+Enter)" style="display: none;"><i class="bi bi-play-circle-fill"></i> 执行选中</button>
                                 <button class="btn btn-secondary" id="import-sql-btn"><i class="bi bi-upload"></i> 导入SQL</button>
                             </div>
                             <span id="status-message" class="text-muted">准备就绪</span>
                         </div>
                    </div>
                    <div class="card results-card">
                        <h6 class="card-header">查询结果</h6>
                        <div class="card-body p-2" id="query-results-container">
                            <div class="text-center text-muted p-4">执行查询后，结果将显示在此处。</div>
                        </div>
                    </div>
                 </div>
            </div>
        </div>
    </div>
</main>

<div class="modal fade" id="import-sql-modal" tabindex="-1">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header"><h5 class="modal-title">导入 SQL 脚本</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
            <div class="modal-body">
                <div class="mb-3"><input class="form-control" type="file" id="sql-file-input" accept=".sql"></div>
                <hr>
                <h6>执行日志</h6>
                <pre id="import-log" class="bg-dark text-white p-2 rounded" style="height: 200px; overflow-y: scroll;"></pre>
            </div>
            <div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button><button type="button" class="btn btn-primary" id="start-import-btn" disabled>开始执行</button></div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/mode/sql/sql.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/addon/hint/show-hint.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.15/addon/hint/sql-hint.min.js"></script>

<script>
// The entire JavaScript block remains unchanged.
document.addEventListener('DOMContentLoaded', () => {
    const API_BASE_URL = 'http://127.0.0.1:5000';

    // --- CodeMirror 初始化与语法补全逻辑 ---
    const editorTextarea = document.getElementById('sql-editor');
    const codeMirrorEditor = CodeMirror.fromTextArea(editorTextarea, {
        mode: 'text/x-sql',
        lineNumbers: true,
        theme: 'default',
        extraKeys: { "Ctrl-Space": "autocomplete" },
        hintOptions: {
            hint: async (cm) => {
                const cursor = cm.getCursor();
                const token = cm.getTokenAt(cursor);
                const query = cm.getRange({ line: 0, ch: 0 }, cursor);

                try {
                    const response = await fetch(`${API_BASE_URL}/api/suggestions`, {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ query: query }),
                    });
                    const data = await response.json();

                    if (data.success && data.suggestions) {
                        const start = token.start;
                        const end = cursor.ch;
                        const currentWord = token.string.trim();

                        const filteredList = data.suggestions.filter(s =>
                            s.toLowerCase().startsWith(currentWord.toLowerCase())
                        );

                        return {
                            list: filteredList.length ? filteredList : data.suggestions,
                            from: CodeMirror.Pos(cursor.line, start),
                            to: CodeMirror.Pos(cursor.line, end)
                        };
                    }
                } catch (error) {
                    console.error("Suggestion fetch error:", error);
                }
            }
        }
    });

    codeMirrorEditor.on('inputRead', (cm, change) => {
        // 自动触发提示，排除分号和空格
        if (/[\w.]/.test(change.text[0])) {
            cm.showHint({ completeSingle: false });
        }
    });
    // --- CodeMirror 逻辑结束 ---

    // --- Theme Switcher Logic ---
    const themeSwitcher = document.getElementById('theme-switcher');
    const themeText = document.getElementById('theme-text');
    const htmlEl = document.documentElement;

    const applyTheme = (theme) => {
        htmlEl.setAttribute('data-bs-theme', theme);
        localStorage.setItem('theme', theme);
        themeText.textContent = theme === 'dark' ? '亮色模式' : '暗色模式';
        // 动态切换 CodeMirror 主题
        codeMirrorEditor.setOption("theme", theme === 'dark' ? 'monokai' : 'default');
    };
    const toggleTheme = () => {
        const currentTheme = htmlEl.getAttribute('data-bs-theme');
        const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
        applyTheme(newTheme);
    };
    themeSwitcher.addEventListener('click', toggleTheme);
    const savedTheme = localStorage.getItem('theme');
    const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    if (savedTheme) {
        applyTheme(savedTheme);
    } else {
        applyTheme(systemPrefersDark ? 'dark' : 'light');
    }

    // --- Original Application Logic ---
    const dbObjectsList = document.getElementById('db-objects-list');
    const executeBtn = document.getElementById('execute-btn');
    const executeSelectedBtn = document.getElementById('execute-selected-btn');
    const statusMessage = document.getElementById('status-message');
    const queryResultsContainer = document.getElementById('query-results-container');
    const importModal = new bootstrap.Modal(document.getElementById('import-sql-modal'));
    const paginationNav = document.getElementById('pagination-nav');
    const paginationContainer = document.getElementById('pagination-container');
    let currentBrowsingTable = '';

    function showEmptyDataBrowserMessage() {
        const dataBrowserContainer = document.getElementById('data-browser-container');
        document.getElementById('table-view-title').textContent = '请从左侧选择一个表进行浏览';
        dataBrowserContainer.innerHTML = `<div class="d-flex h-100 align-items-center justify-content-center text-muted">
            <div class="text-center">
                <i class="bi bi-arrow-left-square-fill fs-1"></i>
                <p class="mt-2">从左侧的数据库对象列表中选择一个表来查看其数据。</p>
            </div>
        </div>`;
        paginationContainer.style.display = 'none';
    }
    async function fetchAndRenderSchema() {
        try {
            const response = await fetch(`${API_BASE_URL}/api/schema`);
            const data = await response.json();
            dbObjectsList.innerHTML = '';
            if (data.success) {
                data.schema.tables.forEach(table => {
                    const li = document.createElement('li');
                    li.innerHTML = `<a class="nav-link">${table.name}</a>`;
                    li.addEventListener('click', (e) => {
                        document.querySelectorAll('#db-objects-list .nav-link').forEach(el => el.classList.remove('active'));
                        e.currentTarget.classList.add('active');
                        browseTable(table.name, 1);
                    });
                    dbObjectsList.appendChild(li);
                });
            } else {
                 dbObjectsList.innerHTML = `<li class="nav-item text-danger p-2">无法加载对象。</li>`;
            }
        } catch (error) {
            console.error('Schema fetch error:', error);
            dbObjectsList.innerHTML = `<li class="nav-item text-danger p-2">错误: ${error.message}</li>`;
        }
    }
    async function browseTable(tableName, page = 1) {
        currentBrowsingTable = tableName;
        new bootstrap.Tab(document.getElementById('data-view-tab')).show();

        document.getElementById('table-view-title').textContent = `表: ${tableName}`;
        const dataBrowserContainer = document.getElementById('data-browser-container');
        dataBrowserContainer.innerHTML = `<table class="table table-striped table-bordered table-hover table-sm mb-0">
                                            <thead class="table-dark sticky-top"></thead>
                                            <tbody></tbody>
                                          </table>`;
        const thead = dataBrowserContainer.querySelector('thead');
        const tbody = dataBrowserContainer.querySelector('tbody');
        thead.innerHTML = '<tr><th>正在加载...</th></tr>';
        tbody.innerHTML = '';
        paginationContainer.style.display = 'block';

        try {
            const response = await fetch(`${API_BASE_URL}/api/table_data/${tableName}?page=${page}&limit=50`);
            const data = await response.json();

            if (!data.success) {
                thead.innerHTML = `<tr><th class="text-danger">错误: ${data.error}</th></tr>`;
                return;
            }

            let headerHtml = '<tr>';
            data.header.forEach(h => headerHtml += `<th>${h}</th>`);
            thead.innerHTML = headerHtml + '</tr>';

            let bodyHtml = '';
            data.rows.forEach(row => {
                bodyHtml += '<tr>';
                row.forEach(cell => bodyHtml += `<td>${cell === null ? '<em>NULL</em>' : cell}</td>`);
                bodyHtml += '</tr>';
            });
            tbody.innerHTML = bodyHtml;

            renderPagination(tableName, data.total_rows, data.page, data.limit);
        } catch (error) {
            thead.innerHTML = `<tr><th class="text-danger">加载错误: ${error}</th></tr>`;
        }
    }
    function renderPagination(tableName, totalRows, currentPage, limit) {
        paginationNav.innerHTML = '';
        const totalPages = Math.ceil(totalRows / limit);
        if (totalPages <= 1) {
             paginationContainer.style.display = 'none';
             return;
        };

        let items = '';
        const createPageItem = (page, text, isDisabled = false, isActive = false) => {
            return `<li class="page-item ${isDisabled ? 'disabled' : ''} ${isActive ? 'active' : ''}">
                        <a class="page-link" data-page="${page}">${text}</a>
                    </li>`;
        };

        items += createPageItem(currentPage - 1, '上一页', currentPage === 1);
        const maxPagesToShow = 5;
        let startPage, endPage;
        if (totalPages <= maxPagesToShow) {
            startPage = 1;
            endPage = totalPages;
        } else {
            if (currentPage <= Math.ceil(maxPagesToShow / 2)) {
                startPage = 1;
                endPage = maxPagesToShow;
            } else if (currentPage + Math.floor(maxPagesToShow / 2) >= totalPages) {
                startPage = totalPages - maxPagesToShow + 1;
                endPage = totalPages;
            } else {
                startPage = currentPage - Math.floor(maxPagesToShow / 2);
                endPage = currentPage + Math.floor(maxPagesToShow / 2);
            }
        }
        if (startPage > 1) items += createPageItem(1, '1') + `<li class="page-item disabled"><span class="page-link">...</span></li>`;
        for (let i = startPage; i <= endPage; i++) {
            items += createPageItem(i, i, false, i === currentPage);
        }
        if (endPage < totalPages) items += `<li class="page-item disabled"><span class="page-link">...</span></li>` + createPageItem(totalPages, totalPages);
        items += createPageItem(currentPage + 1, '下一页', currentPage === totalPages);
        paginationNav.innerHTML = `<ul class="pagination pagination-sm my-0" data-table-name="${tableName}">${items}</ul>`;
    }
    paginationNav.addEventListener('click', (e) => {
        if (e.target.tagName === 'A' && !e.target.parentElement.classList.contains('disabled')) {
            e.preventDefault();
            const page = e.target.dataset.page;
            const tableName = e.target.closest('ul').dataset.tableName;
            if (page && tableName) {
                browseTable(tableName, parseInt(page, 10));
            }
        }
    });

    function handleTextSelection() {
        const hasSelection = codeMirrorEditor.somethingSelected();
        executeSelectedBtn.style.display = hasSelection ? 'inline-block' : 'none';
    }
    async function executeSelectedSQL() {
        const selectedSql = codeMirrorEditor.getSelection().trim();
        if (!selectedSql) return;
        await executeGenericSQL(selectedSql, '正在执行选中部分...');
    }
    async function executeSQL() {
        const sql = codeMirrorEditor.getValue().trim();
        if (!sql) return;
        await executeGenericSQL(sql, '正在执行...');
    }
    async function executeGenericSQL(sql, statusText) {
        statusMessage.textContent = statusText;
        executeBtn.disabled = true;
        executeSelectedBtn.disabled = true;
        queryResultsContainer.innerHTML = '';

        try {
            const response = await fetch(`${API_BASE_URL}/api/execute_sql`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ sql: sql }),
            });
            const data = await response.json();

            if (data.success) {
                statusMessage.textContent = data.message;
                if (data.rows && data.header && data.rows.length > 0) {
                    renderQueryResultTable(data.header, data.rows);
                } else {
                    queryResultsContainer.innerHTML = `<div class="alert alert-success m-2">${data.message}</div>`;
                }
                if (data.schema_modified) {
                    fetchAndRenderSchema();
                }
            } else {
                statusMessage.textContent = '执行失败';
                queryResultsContainer.innerHTML = `<div class="alert alert-danger m-2">${data.error}</div>`;
            }
        } catch (error) {
            statusMessage.textContent = '连接错误';
            queryResultsContainer.innerHTML = `<div class="alert alert-danger m-2">连接错误: ${error}</div>`;
        } finally {
            executeBtn.disabled = false;
            executeSelectedBtn.disabled = false;
            handleTextSelection();
        }
    }

    function renderQueryResultTable(header, rows) {
        let table = '<table class="table table-bordered table-sm table-striped"><thead><tr>';
        header.forEach(h => table += `<th>${h}</th>`);
        table += '</tr></thead><tbody>';
        rows.forEach(row => {
            table += '<tr>';
            row.forEach(cell => table += `<td>${cell === null ? '<em>NULL</em>' : cell}</td>`);
            table += '</tr>';
        });
        table += '</tbody></table>';
        queryResultsContainer.innerHTML = table;
    }
    function setupImportModal() {
        const importSqlBtn = document.getElementById('import-sql-btn');
        const sqlFileInput = document.getElementById('sql-file-input');
        const startImportBtn = document.getElementById('start-import-btn');
        const importLog = document.getElementById('import-log');
        let fileContent = '';

        importSqlBtn.onclick = () => {
            importLog.textContent = '请选择一个 .sql 文件。';
            sqlFileInput.value = '';
            fileContent = '';
            startImportBtn.disabled = true;
            importModal.show();
        };

        sqlFileInput.onchange = async (event) => {
            const file = event.target.files[0];
            if (!file) { startImportBtn.disabled = true; return; }
            try {
                fileContent = await file.text();
                importLog.textContent = `文件 "${file.name}" 已加载。\n\n点击“开始执行”来运行此脚本。`;
                startImportBtn.disabled = false;
            } catch (error) {
                importLog.textContent = `错误：无法读取文件。\n${error}`;
                startImportBtn.disabled = true;
            }
        };

        startImportBtn.onclick = async () => {
            if (!fileContent) return;
            importLog.textContent = '正在连接服务器并执行脚本，请稍候...\n\n';
            startImportBtn.disabled = true;
            sqlFileInput.disabled = true;

            try {
                const response = await fetch(`${API_BASE_URL}/api/execute_script`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ sql: fileContent }),
                });
                const data = await response.json();

                importLog.textContent = '';
                if (data.success) {
                    data.log.forEach(logLine => { importLog.textContent += logLine + '\n'; });
                    importLog.textContent += `\n----------------------------------------\n${data.summary.message}\n`;
                    if (data.summary.schema_modified) {
                        fetchAndRenderSchema();
                    }
                } else {
                    importLog.textContent = `执行失败！\n--------------------\n${data.error}`;
                }
            } catch (error) {
                importLog.textContent += `\n严重错误：无法连接到服务器或请求失败！\n${error}`;
            } finally {
                startImportBtn.disabled = false;
                sqlFileInput.disabled = false;
                importLog.scrollTop = importLog.scrollHeight;
            }
        };
    }

    document.getElementById('refresh-schema-btn').addEventListener('click', fetchAndRenderSchema);
    executeBtn.addEventListener('click', executeSQL);
    executeSelectedBtn.addEventListener('click', executeSelectedSQL);

    codeMirrorEditor.on('cursorActivity', handleTextSelection);
    codeMirrorEditor.on('keydown', (cm, e) => {
        if (e.ctrlKey && !e.shiftKey && e.key === 'Enter') {
            e.preventDefault();
            executeSQL();
        }
        if (e.ctrlKey && e.shiftKey && e.key === 'Enter') {
            e.preventDefault();
            if (codeMirrorEditor.somethingSelected()) {
                executeSelectedSQL();
            }
        }
    });

    setupImportModal();
    fetchAndRenderSchema();
    showEmptyDataBrowserMessage();
});
</script>

</body>
</html>

